package org.example.config;

import com.alibaba.fastjson2.JSONObject;
import org.example.otherObject.ResponseObject;
import org.example.util.CommonParam;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 首先 这里虽然用了security 但是只是简单的应用
 * 这个项目所有的技术都是简单的应用
 *
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommonParam {
    /**
     * 处理security对静态资源的拦截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    /**
     * 因为现在这个项目已经写了一些了 如果我们用定义一种认证形式  认证成功后会会经过自定义的成功路径
     * 我们需要让security中的securityFilterChain中的所有的过滤器放行  执行我们controller中的登录逻辑
     * 以前认证成功后security会将Authentication存储到SecurityContextHolder中 这里我们需要自己处理 主要是为了授权路径使用
     * 如何跳过认证呢？
     * 我们不要配置登录的资源路径 security就不会拦截   FilterChainProxy就不会拦截请求  直接放行
     * 所以这里就别动了
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }


    //不能配置登录相关的配置 不然会被security拦截  然后一系列认证 就不会走我们的controller逻辑了
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置授权路径  这里我也不弄多了  试一下就行
        http.authorizeRequests().antMatchers(
                "/comment/**",//所有的评论需要登录  登录后有权限
                "/discussPost/save",
                "/discussPost/detail/**",
                "/follow/**",
                "/like/**",
                "/message/**",
                "/user/setting.html",
                "/user/logout",
                "/user/headerImgUpload",
                "/user/updatePassword",
                "/user/userPage.html",
                "/generate/web/image",
                "/download/generate/image"
        ).hasAnyAuthority(USER_AUTHORITY_NORMAL,USER_AUTHORITY_MANAGER,USER_AUTHORITY_HOUSE)
                .antMatchers("/discussPost/top","/discussPost/digest").hasAnyAuthority(USER_AUTHORITY_MANAGER,USER_AUTHORITY_HOUSE)
                .antMatchers("/discussPost/delete").hasAnyAuthority(USER_AUTHORITY_HOUSE)
                .antMatchers("/count/dataPage","/count/uvSearch","/count/dauSearch").hasAnyAuthority(USER_AUTHORITY_MANAGER)
                .anyRequest()
                .permitAll()

        ;
        //配置没有登录 访问授权资源时的处理 和 没有权限访问的处理
        http.exceptionHandling()
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                        //这里我其实有点没有想到 就是这个请求资源 可以是正常的请求 也 可以是ajax请求  ajax请求一般返回字符串 必须要一个页面所以这里判断一下请求类型
                        //我是不清楚的 老师讲的 ajax请求会在请求头中有一个key X-Requested-With value如果是XMLHttpRequest 就是ajax请求
                        String header = httpServletRequest.getHeader("X-Requested-With");
                        if (header!=null && header.equals("XMLHttpRequest")){
                            //ajax请求  返回一个没有权限的字符串提示
                            ResponseObject responseObject = new ResponseObject();
                            responseObject.setCode("403");//服务端拒绝请求一般是403响应码
                            responseObject.setMessage("抱歉!您的用户权限不够,不能进行访问");
                            httpServletResponse.setContentType("application/json;charset=urf-8");
                            httpServletResponse.setCharacterEncoding("UTF-8");
                            PrintWriter writer = httpServletResponse.getWriter();
                            writer.write(JSONObject.toJSONString(responseObject));
                        }else {
                            //正常的请求 返回一个页面
                            httpServletRequest.setAttribute("noAuthority","亲爱的用户，您的访问权限不足");
                            httpServletRequest.getRequestDispatcher("/common/SecurityAccessException").forward(httpServletRequest,httpServletResponse);
                        }
                    }
                })//这个是处理访问授权资源 登录了没有权限的处理器
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        String header = httpServletRequest.getHeader("X-Requested-With");
                        if (header!=null && header.equals("XMLHttpRequest")){
                            //ajax请求  返回一个没有权限的字符串提示
                            ResponseObject responseObject = new ResponseObject();
                            httpServletResponse.setCharacterEncoding("UTF-8");
                            responseObject.setCode("403");//服务端拒绝请求一般是403响应码
                            responseObject.setMessage("抱歉！您还没有登录,请登录");
                            httpServletResponse.setContentType("application/json;charset=urf-8");
                            PrintWriter writer = httpServletResponse.getWriter();
                            writer.write(JSONObject.toJSONString(responseObject));
                        }else {
                            //正常的请求 返回一个页面
                            httpServletRequest.setAttribute("noLogin","亲爱的用户，您还没有登录");
                            httpServletRequest.getRequestDispatcher("/common/SecurityAccessException").forward(httpServletRequest,httpServletResponse);
                        }
                    }
                });//这个是处理访问授权资源时 没有登录的处理器
        //因为security是默认开启了csrf(cross site request forgery)防护
        //所有的请求request中都有一个参数_csrf 如果你用form表单提交信息 默认会带上 放在参数中
        //如果是ajax请求 不会带上  我们需要自己设置所有的ajax请求头 添加一个token 麻烦 所以就先关闭
        http.csrf().disable();
        //还有两个问题 一个退出 一个登录
        //退出：security默认拦截logout请求 我们不用他的处理 我们随便写一个拦截请求 他揽这个 就会放过  就执行我们的退出逻辑 我们的user退出请求为/user/logout 他会拦截(我们需要自动处理SecurityContextHolder中的用户清除)
        //登录：如果你没有配置login配置 就不会通过security的认证 就直接到我们的springMVC 需要自己处理SecurityContextHolder 中的信息存储

    }
}
